import type { PatchState } from "@rivet/state";
import type { Editor } from "./editor.ts";
import {
  createSelectorCreator,
  type CreateSelectorFunction,
  lruMemoize,
} from "reselect";
import type { AnySelectors, SingleSelectors } from "./types.ts";
import { isEqual } from "es-toolkit";
/*
查询生成器
 缓存查询结果，当参数参数没有改变，则直接返回缓存结果，否则重新计算
*/
export class SelectorManager {
  editor: Editor;
  createSelectorLru: CreateSelectorFunction;
  rawSelectors: AnySelectors;
  singleSelectors: SingleSelectors;
  selectorStorage: any = {};
  constructor(editor: Editor) {
    this.editor = editor;
    this.rawSelectors = this.editor.extensionManager.selectors;
    this.createSelectorLru = createSelectorCreator({
      memoize: lruMemoize,
      memoizeOptions: {
        equalityCheck: isEqual,
        resultEqualityCheck: isEqual,
        maxSize: editor.options.lruMaxSize,
      },
      argsMemoize: lruMemoize,
      argsMemoizeOptions: {
        equalityCheck: isEqual,
        resultEqualityCheck: isEqual,
        maxSize: editor.options.lruMaxSize,
      },
    });
    this.singleSelectors = this.buildSelectors();
  }

  get state(): PatchState {
    return this.editor.state;
  }
  get selectors(): SingleSelectors {
    return this.singleSelectors;
  }
  buildSelectors(): SingleSelectors {
    const { rawSelectors, editor } = this;

    return Object.fromEntries(
      Object
        .entries(rawSelectors)
        .map(([name, selector]) => {
          const method = (...args: any[]) => {
            const key = name + JSON.stringify(args);
            if (this.selectorStorage[key]) {
              return this.selectorStorage[key](editor.state);
            } else {
              const selectorItem = selector(...args);
              let  callback;
              if(typeof selectorItem =="function"){
                 callback = this.createSelectorLru(
                  [(state: PatchState)=>state.doc],
                  selectorItem,
                );
              }else{
                 callback = this.createSelectorLru(
                  selectorItem.dependency,
                  selectorItem.selector,
                );
              }
              this.selectorStorage[key] = callback;
              return this.selectorStorage[key](editor.state);
            }
          };
          return [name, method];
        }),
    ) as unknown as SingleSelectors;
  }
}
